#include "syscall.h"
typedef int bool;

#define true 1
#define false 0
#define NULL 0

#define MAX_CRNum 3 
#define MAX_ClerkNum 3
#define MAX_StockBoyNum 3
#define MAX_CustomerNum 20

#define MinPrices 1
#define PriceFactor 100

/*Environment Varible*/
int CRNum;
int ClerkNum;
int StockBoyNum;
int CustomerNum;
#define AisleNum 5
#define ItemNum 10

/*Tricks*/
/*These Varibles is used to create specific scenario for test cases*/
int ReturnChance;					/*The chance if a customer to be return customer*/
int LostChance;						/*The chance a customer fails to find an item*/
int OriginItemAmount;					/*Origin Amount of each Item*/
int MinBoughtEach;					/*Min number of items a customer can buy*/
int MaxBoughtEach;					/*Max number of items a customer can buy is [MinBoughtEach+MaxBoughtEach-1]*/
int BornLazy;						/*If a clerk will go to rest as soon as he is created?*/
/*int PriceFactor;*/					/*The Factor that affects randomly generated prices*/
/*int MinPrice;*/						/*The Mininum price can be generated*/
bool ManagerTrackLine;					/*Does Manager keep track of Line himself*/
bool CustomerBusyWaiting;
int ProgramRun;
int TotalSales;						/*record the total sales amount.*/

/*Locks*/
int initLock;
int idLock;							/*Lock to pretect id from race condition*/
int OpLock1;							/*StartLine Exclusion*/
int OpLock2;							/*StockBoy Break Exclusion*/
int OpLock3;							/*Clerk Break Exclusion*/
int OpLock4;							/*Leaving Customer Exclusion*/
int OpLock5;							/*For StockRoom Condition Check	*/
int TableLock[MAX_CRNum];				/*Acquire this Lock before handle items on table*/
int CheckQueue;							/*Acquire this lock before check Line Lengths*/
int ScreenLock[MAX_CRNum];				/*Acquire this Lock before interact with cash register machine*/
int StockRoomLock;						/*The Lock on StockRoom*/
int ServiceButtonLock;					/*The Lock to protect service button*/
int ClerkCallLock;						/*The Lock to protect clerk call flag*/
int StockBoyStatusLock;					/*The Lock to protect Stock Boy Status*/
int AisleHelpLock[AisleNum];		/*The Lock to protect AisleHelp related monitor varibles*/
int AisleLock[AisleNum];			/*Acquire this Lock before access any item on an aisle*/
int ServiceStateLock[MAX_StockBoyNum];	/*The Lock to protect help state between a stock boy and a customer*/

/*CVs*/
int StartLine;							/*the start line*/
int ClerkOnBreak;						/*The Condition that clerks on break wait on*/
int StockBoyOnBreak;					/*The Condition that stockboys on break wait on*/
int Queue[MAX_CRNum];					/*Paying Lines*/
int Screen[MAX_CRNum];					/*The cash register machine screen State Condition*/
int Table[MAX_CRNum];					/*The Cashier Table State Condition*/
int LackMoney[MAX_CRNum];				/*If a cash register lacks money to pay return customer, he waits for Manager here*/
int StockRoomCV;						/*The Condition on which Stock Boy wait for room*/
int WaitForRestockCV[ItemNum];		/*Customers waiting for item to be restocked. It is protected by AisleLock*/
int ServiceStateCV[MAX_StockBoyNum];	/*The State Condition when a stock boy is helping a customer*/
int AisleHelpCV[AisleNum];			/*The Condition a customer waits on before a stock boy comes to serve him*/
/*Synchronization Define End*/

/*Shared Varible End*/
int AisleNeedRestock;	/*record if an aisle need to be restocked*/
int ServiceButtonOn;	/*false*/
			/*If somebody pressed service button?*/
int ClerkCall;  	/*false;*/					/*If somebody ask for more clerk?*/
int ClerkCallId;					/*The Clerk who asked manager to wake another clerk*/

int QueueLength;			/*Lengths of paying Lines*/
int ClerkOnWork;			/*If a Clerk is ready for customer?*/
		
int TableArea;				/*Place to put items*/
int CashArea;				/*Place to put money*/

int data;				/*The data is the place to exchange information with cash register*/
int MoneyCollected;			/*The money each cash register earned before manager empty it*/
int TotalMoneyCollected;		/*The money each cash register ever earned*/
int MoneyLacked;			/*The money a cash register lacks to pay a return customer*/

int StockBoyInRoom;			/*Id of StockBoy currently in Stock Room*/

int StockBoyStatus;			/*Each StockBoy's status.If he is in an aisle, status is the aisle number or aisle+AisleNum; Otherwise,  -1*/
int AisleHelpLineLength;		/*The number of customer waiting for help on each aisle*/
int Booklet;				/*The booklet used by Customer and StockBoy to exchange information*/

int CustomerLeaving;			/*Total number of customers that finished shopping and left*/

int DidSomething;                        /*[MAX_StockBoyNum];*/
int Prices;
int ItemAmount;
int ItemRestockPrediction;

/*ID Assignment*/
int nextManagerID;
int nextCustomerID;
int nextClerkID;
int nextStockBoyID;
int nextCashRegisterID;

int GenRandom(int limit)
{
	return Random(limit);
}

int modifyName(char * name, int offset, int num)
{
	int i=0;
	int pos=0;
	int digit;
	if(name == NULL) return -1;
	while(name[i]!='\0' && i<offset+4){
		i++;
	}
	if(name[i]=='\0' && i < offset+4) return -1;
	pos= i-1;
	while(num!=0){
		digit=num%10;
		num = (num-digit)/10;
		name[pos]=(char)(digit+48);
		pos--;
	}
	for(;pos>=offset;pos--)
		name[pos]='0';
	return 1;
}

void initialize()
{
	char* name;
	int numbers[6];
	int i;
		
	nextManagerID = CreateMV("nextManagerID",13,1,0);
	nextCustomerID = CreateMV("nextCustomerID",14,1,0);
	nextClerkID = CreateMV("nextClerkID",11,1,0);
	nextStockBoyID = CreateMV("nextSBID",14,1,0);
	nextCashRegisterID = CreateMV("nextCRID",18,1,0);

	CRNum = GetMV(CreateMV("CRNum",5,1,MAX_CRNum),0);
	ClerkNum = GetMV(CreateMV("ClerkNum",8,1,MAX_ClerkNum),0);
	StockBoyNum = GetMV(CreateMV("StockBoyNum",11,1,MAX_StockBoyNum),0);
	CustomerNum = GetMV(CreateMV("CustomerNum",11,1,MAX_CustomerNum),0);	

	OpLock1 = RemoteCreateLock("OperationLock1",15); 		
	OpLock2 = RemoteCreateLock("OperationLock2",15);
	OpLock3 = RemoteCreateLock("OperationLock3",15);
	OpLock4 = RemoteCreateLock("OperationLock4",15);
	OpLock5 = RemoteCreateLock("OperationLock5",15);
	idLock= RemoteCreateLock("IDLock",7);
	StartLine =  RemoteCreateCondition("StartLine",10);
	ClerkOnBreak =  RemoteCreateCondition("ClerkBreak",11);
	StockBoyOnBreak =  RemoteCreateCondition("StockBoyBreak",14);
	CheckQueue = RemoteCreateLock("CheckQueueLock",15);
	StockRoomLock = RemoteCreateLock("StockRoomLock",14);
	StockRoomCV = RemoteCreateCondition("StockRoomCV",12);
	ServiceButtonLock = RemoteCreateLock("ServiceButtonLock",18);
	StockBoyStatusLock = RemoteCreateLock("StockBoyStatusLock",19);
	ClerkCallLock=RemoteCreateLock("ClerkCallLock",14);

	/*MV initialize*/
	ReturnChance = CreateMV("ReturnChance",12,1,20);	/*The chance if a customer to be return customer*/
	LostChance = CreateMV("LostChance",10,1,25);		/*The chance a customer fails to find an item*/
	OriginItemAmount = CreateMV("OriginItemAmount",16,1,3);	/*Origin Amount of each Item*/
	MinBoughtEach = CreateMV("MinBoughtEach",13,1,1);	/*Min number of items a customer can buy*/
	MaxBoughtEach = CreateMV("MaxBoughtEach",13,1,10);	/*Max number of items a customer can buy is [MinBoughtEach+MaxBoughtEach-1]*/
	/*PriceFactor = CreateMV("PriceFactor",11,1,100);*/		/*The Factor that affects randomly generated prices*/
	/*MinPrice = CreateMV("MinPrice",8,1,0);*/			/*The Mininum price can be generated*/
	ManagerTrackLine = CreateMV("ManagerTrackLine",16,1,true);/*Does Manager keep track of Line himself*/
	
	ProgramRun=CreateMV("ProgramRun",10,1,true);
	TotalSales=CreateMV("TotalSales",10,1,0);
	CustomerBusyWaiting = CreateMV("CustomerBusyWaiting",19,1,false);
	ServiceButtonOn=CreateMV("ServiceButtonOn",15,1,0);
	ClerkCall=CreateMV("ClerkCall",9,1,0);
	ClerkCallId=CreateMV("ClerkCallId",11,1,-1);
       	StockBoyInRoom=CreateMV("StockBoyInRoom",14,1,-1);
       	CustomerLeaving=CreateMV("CustomerLeaving",15,1,0);
	
	/*For CR-related MV*/
	QueueLength=CreateMV("QueueLength",11,CRNum,0);
	ClerkOnWork=CreateMV("ClerkOnWork",11,CRNum,1);
	BornLazy=CreateMV("BornLazy",8,CRNum,0);
	TableArea=CreateMV("TableArea",9,CRNum,-1);	
	CashArea=CreateMV("CashArea",8,CRNum,0);
	data=CreateMV("data",4,CRNum,-1);			/*-1 is used to denote initial state for cash register*/
	MoneyCollected=CreateMV("MoneyCollected",14,CRNum,0);
	TotalMoneyCollected=CreateMV("TotalMoneyCollected",19,CRNum,0);
	MoneyLacked=CreateMV("MoneyLacked",11,CRNum,-1);

	/*For stockboy-related MV*/
	Booklet=CreateMV("Booklet",7,StockBoyNum,-1);	
	StockBoyStatus = CreateMV("StockBoyStatus",14,StockBoyNum,-1);/*StockBoyStatus == -1 means he is out of any aisle*/
	DidSomething=CreateMV("DidSomething",12,StockBoyNum,false);

	/*For aisle-related MV*/
	AisleNeedRestock=CreateMV("AisleNeedRestock",16,AisleNum,0);	/*no aisle need to be restocked at very beginning*/
	AisleHelpLineLength=CreateMV("AisleHelpLineLength",19,AisleNum,0);/*originally no customer is waiting for help in any aisle*/
	/*Initialization of array varibles declared*/

	/*For item-related MV*/
	Prices=CreateMV("Prices",6,ItemNum,0);
	ItemRestockPrediction=CreateMV("ItemRestockPrediction",21,ItemNum,0);/*Item to be restocked is 0	*/
	ItemAmount = CreateMV("ItemAmount",10,ItemNum,3);	/*Origin Amount of each Item*/
	
	for(i=0;i<CRNum;i++)
	{
		/*char* name;*/
		name="Queue0000";
		modifyName(name,5,i);	
		Queue[i] = RemoteCreateCondition(name,9);
		
		name="TableLock0000";
		modifyName(name,9,i);
		TableLock[i]=RemoteCreateLock(name,13);
		
		name="Table0000";
		modifyName(name,5,i);
		Table[i]=RemoteCreateCondition(name,9);
		
		name="ScreenLock0000";
		modifyName(name,10,i);
		ScreenLock[i]=RemoteCreateLock(name,14);
			
		name="Screen0000";
		modifyName(name,6,i);
		Screen[i]=RemoteCreateCondition(name,10);
		
		name="Lack0000";
		modifyName(name,4,i);
		LackMoney[i]=RemoteCreateCondition(name,8);
	}

	for(i=0;i<StockBoyNum;i++)
	{
		name="ServiceStateLock0000";
		modifyName(name,16,i);
		ServiceStateLock[i]=RemoteCreateLock(name,20);
		
		name="ServiceState0000";
		modifyName(name,12,i);
		ServiceStateCV[i]=RemoteCreateCondition(name,16);
	}

	for (i=0;i<AisleNum;i++)
	{
		name="AisleHelpLock0000";
		modifyName(name,13,i);
		AisleHelpLock[i]=RemoteCreateLock(name,17);
		
		name="AisleHelpCV0000";
		modifyName(name,11,i);
		AisleHelpCV[i]=RemoteCreateCondition(name,15);
		
		name="AisleLock0000";
		modifyName(name,9,i);
		AisleLock[i]=RemoteCreateLock(name,13);
	}
	
	for(i=0;i<ItemNum;i++)
	{
		name="WaitForRestockCV0000";
		modifyName(name,16,i);
		WaitForRestockCV[i]=RemoteCreateCondition(name,20);
	}
}


/*---------------------CashReg Minor---------------------*/
/*CashRegister Behavior*/
/*	the main Logic of a CashRegister*/
/*  by OceanMaster*/
/*-------------------------------------------------------*/
	
int
CheckInput(int id)										/*check input from clerk*/
{
	int i;
	RemoteSignalCondition(Screen[id],ScreenLock[id]);
	RemoteWaitCondition(Screen[id],ScreenLock[id]);
	i = (int)GetMV(data,id);
	return i;
}

void
DisplayTotal(int id, int total)						/*Display total cost to clerk*/
{
	SetMV(data,id,total);
	RemoteSignalCondition(Screen[id],ScreenLock[id]);
	RemoteWaitCondition(Screen[id],ScreenLock[id]);
}

/*---------------------CashReg Major---------------------*/
/*CashRegister Behavior*/
/*	the main Logic of a CashRegister*/
/*  by OceanMaster*/
/*-------------------------------------------------------*/
void 
CashRegisterBehavior()
{
	int id;
	int numbers[6];
	/*WriteFormat("begin CR behavior\n",19,0,0);*/
	RemoteAcquireLock(idLock);
	id=GetMV(nextCashRegisterID,0);
	SetMV(nextCashRegisterID,0,id+1);
	RemoteReleaseLock(idLock);
	
	RemoteAcquireLock(ScreenLock[id]);							/*The Lock controls interaction with cash register*/
	
	/*printf("CashMachine %s: Trun On\n",name);*/

	while(GetMV(ProgramRun,0))									/*As long as programming is running..*/
	{
		/*printf("CashMachine %s: Ready for input\n",name);*/
	
		int total = 0;								/*Assuming a new transcation starts*/
		int cusId;					/*Get customer id*/
		bool retCustomer;			/*Get customer purpose : return customer or not*/

		int item;					/*Scan a new item*/
		int amount = 0;									/*Store total amount*/
		
		WriteFormat("CashRegister [%d] Running...\n",30,&id,1);
		cusId = CheckInput(id);

		if(cusId == -100)			/*Special key set by manager to stop machine*/
			continue;

		retCustomer = CheckInput(id);	
		item = CheckInput(id);		
		
		while(item!=-1)									/*If it isn't -1; (Item is -1 means all item is scanned, should display Total amount)*/
		{
			amount++;									/*count total amount of items*/
			total +=GetMV(Prices,item);						/*Add item's price to total cost*/
			numbers[0]=id;
			numbers[1]=item;
			numbers[2]=id;
			WriteFormat("Cash Register [%d] got item [%d] from clerk [%d] to scan.\n", 59, numbers,3);
			item = CheckInput(id);						/*Scan a new item*/
		}
		
		numbers[0]=id;
		numbers[1]=amount;
		numbers[2]=id;
		WriteFormat("Cash Register [%d] has scanned all [%d] items for Customer [%d]\n", 65, numbers, 3);
		numbers[0]=id;
		numbers[1]=total/10;
		numbers[2]=total%10;
		numbers[3]=cusId;
		numbers[4]=id;
		numbers[5]=amount;
		WriteFormat("Cash Register [%d] gave total cost $[%d.%d] for Customer [%d] to Clerk[%d] for [%d] items.\n", 95, numbers, 6);
		
		DisplayTotal(id,total);							/*Display total Amount to screen*/
		
		if(retCustomer)									/*We have to prepare cash for return customer*/
		{
			if((GetMV(MoneyCollected,id)-total)>=0)			/*If MoneyCollected enough to pay customer*/
			{
				SetMV(MoneyCollected,id,GetMV(MoneyCollected,id)-total);				/*Pay it directly*/
			}
			else										/*Otherwise, we have to count on manager to solve the problem*/
			{
				int lacked = total - GetMV(MoneyCollected,id);	/*Count how much I lack*/
				SetMV(MoneyCollected,id,0);						/*Empty Money Collected to pay customer*/
				
				numbers[0]=id;
				numbers[1]=lacked/10;
				numbers[2]=lacked%10;
				WriteFormat("Cash Register [%d] informed the Manager that he needs $[%d.%d].\n",68,numbers,3);
				SetMV(MoneyLacked,id,lacked);					/*store amount of money lacked*/
				RemoteWaitCondition(LackMoney[id],ScreenLock[id]);			/*Wait for manager to come*/
				
				
				WriteFormat("Cash Register [%d] received $[%d.%d] from the Manager.\n",59, numbers,3);
			}
		
			SetMV(data,id,total);								/*display total*/
			
			numbers[0]=id;
			numbers[1]=id;
			WriteFormat("Cash Register [%d] gave the money to Clerk [%d].\n",50,numbers, 2);
			
			RemoteSignalCondition(Screen[id],ScreenLock[id]);		/*provided cash*/
			RemoteWaitCondition(Screen[id],ScreenLock[id]);		/*wait for clerk to transmit money*/
		}
		else											/*If it is normal customer, now cash should have been put in front of machine*/
		{
			SetMV(MoneyCollected,id,GetMV(MoneyCollected,id)+total);					/*take cash in machine balance*/
			
			numbers[0]=id;
			numbers[1]=id;
			WriteFormat("Cash Register [%d] got the money from Clerk [%d].\n", 51, numbers, 2);
			
			RemoteSignalCondition(Screen[id],ScreenLock[id]);		/*tell clerk money has been saved*/
			RemoteWaitCondition(Screen[id],ScreenLock[id]);		/*wait for command to provide receipt*/
		}
		numbers[0]=id;
		numbers[1]=id;
		numbers[2]=cusId;
		WriteFormat("Cash Register [%d] gave receipt to Clerk[%d] for Customer [%d].\n", 65, numbers, 3);
		RemoteSignalCondition(Screen[id],ScreenLock[id]);			/*provided receipt*/
		
		numbers[1]=GetMV(MoneyCollected,id)/10;
		numbers[2]=GetMV(MoneyCollected,id)%10;
		WriteFormat("Cash Register [%d] total sale is $[%d.%d].\n",47, numbers, 3);
	}
	RemoteReleaseLock(ScreenLock[id]);
	
	Exit(0);
}


int main()
{
	initialize();

	CashRegisterBehavior();

}
